package com.irdstudio.bsp.executor.rest.schedule;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

import com.irdstudio.bsp.executor.core.dao.SSrvsCronConfDao;
import com.irdstudio.bsp.executor.core.dao.SSrvsCronHisDao;
import com.irdstudio.bsp.executor.core.dao.domain.SSrvsCronConf;
import com.irdstudio.bsp.executor.core.dao.domain.SSrvsCronHis;
import com.irdstudio.bsp.executor.core.plugin.dubbo.DubboClient;
import com.irdstudio.bsp.executor.core.tinycore.jdbc.dbcp.TConnPool;
import com.irdstudio.bsp.executor.rest.init.ExecutorInstInfo;
import com.irdstudio.bsp.executor.rest.job.ScheduleJob;
import org.apache.log4j.Logger;
import org.quartz.CronExpression;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import com.alibaba.dubbo.rpc.service.GenericService;
import com.irdstudio.sdk.beans.core.util.TimeUtil;

/**
 * Quartz作业执行引擎(所有作业均由此类启动)
 * @author ligm
 * @date 2018-06-16
 */
public class QuartzJobEngine implements Job{

	private static Logger logger = Logger.getLogger(QuartzJobEngine.class);

	@Override
	public void execute(JobExecutionContext jobExecutionContext)
			throws JobExecutionException {
		Connection conn = null;
		String jobCode = jobExecutionContext.getJobDetail().getKey().getName();
		SSrvsCronConfDao sSrvsCronConfDao =null;
		SSrvsCronConf sSrvsCronConf =null;
		SSrvsCronHisDao sSrvsCronHisDao=null;
		SSrvsCronHis sSrvsCronHis=null;
		// 作业执行开始计时
		long startMills = System.currentTimeMillis();
		try {
			// 初始化并获取定时调度任务实例信息
			conn = TConnPool.getDefaultPool().getConnection();
			sSrvsCronConfDao =new SSrvsCronConfDao(conn);
			SSrvsCronConf cronConf=new SSrvsCronConf();
			cronConf.setJobCode(jobCode);
			sSrvsCronConfDao.queryByPK(cronConf);
			sSrvsCronConf = sSrvsCronConfDao.queryByPK(cronConf);
			// 如果没有实例,说明尚未初始化,不可以运行，跳出
			if (sSrvsCronConf == null) {
				logger.info(jobCode + "尚未初始化!");
				return;
			}
			// 如果当前Agent与要求的Agent不匹配,也不运行该定时调度任务,跳出
			if(!ExecutorInstInfo.EXECUTOR_ID.equals(sSrvsCronConf.getAgentId())){
				StringBuffer sb = new StringBuffer(sSrvsCronConf.getJobDesc());
				sb.append("(").append(jobCode).append(")只能由")
						.append(sSrvsCronConf.getAgentId()).append("运行!!!");
				logger.error(sb);
				return;
			}

			// 如果实例已经运行,则跳过本次定时调度
			if (ScheduleJobUtil.STATE_RUNNING.equals(sSrvsCronConf.getJobState())) {
				StringBuffer sb = new StringBuffer(sSrvsCronConf.getJobDesc());
				sb.append("(").append(jobCode).append(")正在")
						.append(sSrvsCronConf.getAgentId()).append("运行!");
				logger.info(sb);
				return;
			}
			// 如果实例已经禁用,将任务移除
			if (ScheduleJobUtil.STATE_FALSE.equals(sSrvsCronConf.getJobState())) {
				QuartzManager.removeJob(jobCode,ScheduleJobUtil.JOB_GROUP_NAME,ScheduleJobUtil.TRIGGER_NAME + "-" + sSrvsCronConf.getJobCode(),ScheduleJobUtil.TRIGGER_GROUP_NAME);
				StringBuffer sb = new StringBuffer(sSrvsCronConf.getJobDesc());
				sb.append("(").append(jobCode).append(")在")
						.append(sSrvsCronConf.getAgentId()).append("被禁用,已移出!");
				logger.info(sb);
				return;
			}

			// 开始执行
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			CronExpression cronExpression = new CronExpression(sSrvsCronConf.getCronExpression());

			//将作业状态更新为运行中
			logger.info(new StringBuffer("开始执行作业[").append(jobCode).append("]..."));

			sSrvsCronConf.setLastExecuteTime(sdf.format(jobExecutionContext.getFireTime()));
			sSrvsCronConf.setNextExecuteTime(sdf.format(cronExpression.getNextValidTimeAfter(new Date())));
			sSrvsCronConf.setJobState(ScheduleJobUtil.STATE_RUNNING);
			sSrvsCronConfDao.updateByPk(sSrvsCronConf);
			//添加一条数据到定时任务历史表中
			sSrvsCronHisDao=new SSrvsCronHisDao(conn);
			sSrvsCronHis=new SSrvsCronHis();
			sSrvsCronHis.setRecordId(createUUIDKey());
			sSrvsCronHis.setJobCode(jobCode);
			sSrvsCronHis.setJobDesc(sSrvsCronConf.getJobDesc());
			sSrvsCronHis.setStartTime(TimeUtil.getCurrentDateTime());
			sSrvsCronHis.setAgentId(sSrvsCronConf.getAgentId());
			logger.info("执行作业类:" + sSrvsCronConf.getJobClass() + ",方法:" + sSrvsCronConf.getJobMethod());
			if (ScheduleConstant.JOB_CLASS_HSF.equals(sSrvsCronConf.getJobClassType())) {

				logger.info("HSF服务方法调用开始......");
				// 按HSF方式调用作业类
				/*HSFSpringConsumerBean consumerBean = new HSFSpringConsumerBean();
				consumerBean.setInterfaceName(cronConf.getJobClass());
				consumerBean.setVersion(cronConf.getServiceVersion());
				consumerBean.setGeneric("true"); // 设置 generic 为 true
				// 设置组别
				if (StringUtils.isNotEmpty(cronConf.getServiceGroup())) {
					consumerBean.setGroup(cronConf.getServiceGroup());
				} else {
					consumerBean.setGroup("HSF");
				}
				// 设置超时
				if (cronConf.getServiceTimeout() != null) {
					consumerBean.setClientTimeout(cronConf.getServiceTimeout().intValue());
				}

				//初始化
				consumerBean.syncInit();
				GenericService svc = (GenericService) consumerBean.getObject(); // 强转接口接口为 GenericService

				// 泛化接口调用
				Object ivkResult = svc.$invoke(cronConf.getJobMethod(), null, null);
				logger.info("service interface: " + cronConf.getJobClass() + ", service version: " + cronConf.getServiceVersion() +
						", service group: " + cronConf.getServiceGroup() + ", service timeout: " + cronConf.getServiceTimeout() +
						", invoke result :" + ivkResult);
				logger.info("HSF服务方法调用结束！");*/

			} else if (ScheduleConstant.JOB_CLASS_DUBBO.equals(sSrvsCronConf.getJobClassType())) {
				// 按DUBBO方式调用作业类
				logger.info("DUBBO服务方法调用开始......");
				// 强转接口接口为 GenericService
				GenericService svc = null;
				if(sSrvsCronConf.getServiceTimeout() == null) {
					//如果不设置超时时间，则采用system.propeties配置的超时时间
					svc = DubboClient.getDubboClient().buildGenericService(sSrvsCronConf.getJobClass(),
							sSrvsCronConf.getServiceGroup(),
							sSrvsCronConf.getServiceVersion());
				} else {
					svc = DubboClient.getDubboClient().buildGenericService(sSrvsCronConf.getJobClass(),
							sSrvsCronConf.getServiceTimeout().intValue(),
							sSrvsCronConf.getServiceGroup(),
							sSrvsCronConf.getServiceVersion());
				}
				//默认不传参数
				String[] paramTypeArr = null;
				Object[] paramValueArr = null;
				//调用执行
				logger.info("interface: " + sSrvsCronConf.getJobClass()
						+ ",version: " + sSrvsCronConf.getServiceVersion() + ", group: " + sSrvsCronConf.getServiceGroup() + ",timeout: " + sSrvsCronConf.getServiceTimeout()
						+ ", param type: " + paramTypeArr + ", param value: " + paramValueArr);
				Object ivkResult = svc.$invoke(sSrvsCronConf.getJobMethod(), paramTypeArr, paramValueArr);
				logger.info("service call ivkResult: " + ivkResult);
				logger.info("DUBBO服务方法调用结束");
			} else {
				// 按本地类调用,可以是任意类(需要指定方法)
				logger.info("本地方法" + sSrvsCronConf.getJobClass() + "调用开始......");
				Object job = Class.forName(sSrvsCronConf.getJobClass()).newInstance();
				if(ScheduleJob.class.isInstance(job)) {
					((ScheduleJob) job).doExcetue(conn, sSrvsCronConf);
					logger.info("ScheduleJob -- 本地方法调用结束！");
				} else {
					Method method = job.getClass().getMethod(sSrvsCronConf.getJobMethod());
					method.invoke(job, null);
					logger.info("本地方法调用结束！");
				}
			}
			// 将作业状态更改为执行成功,并计算耗时,并将记录复制到历史表
			sSrvsCronHis.setResultDesc("successful");
			sSrvsCronHis.setState("Y");
			updateStateToEnd(sSrvsCronConfDao, sSrvsCronConf, sSrvsCronHisDao, sSrvsCronHis, startMills);
			logger.info(new StringBuffer("执行作业[").append(jobCode).append(
					"]完成..."));

		} catch (SQLException e) {
			logger.error(e);
			// 将作业状态更改为执行有错误,并计算耗时,并将记录复制到历史表
			sSrvsCronHis.setResultDesc(e.getMessage());
			sSrvsCronHis.setState("N");
			try {
				updateStateToEnd(sSrvsCronConfDao, sSrvsCronConf, sSrvsCronHisDao, sSrvsCronHis, startMills);
			} catch (SQLException throwables) {
				throwables.printStackTrace();
			}

			logger.info(new StringBuffer("执行作业[").append(jobCode).append(
					"]发生错误..."));
		} catch (Exception e) {
			logger.error(e);
			// 将作业状态更改为执行有错误,并计算耗时,并将记录复制到历史表
			sSrvsCronHis.setResultDesc(e.getMessage());
			sSrvsCronHis.setState("N");
			try {
				updateStateToEnd(sSrvsCronConfDao, sSrvsCronConf, sSrvsCronHisDao, sSrvsCronHis, startMills);
			} catch (SQLException throwables) {
				throwables.printStackTrace();
			}

			logger.info(new StringBuffer("执行作业[").append(jobCode).append(
					"]发生错误..."));
		} finally {
			// 释放连接资源
			TConnPool.getDefaultPool().releaseConnection(conn);
		}

	}

	/**
	 *
	 * @param sSrvsCronConfDao
	 * @param sSrvsCronConf
	 * @param sSrvsCronHisDao
	 * @param sSrvsCronHis
	 * @param startMills
	 */
	private void updateStateToEnd(SSrvsCronConfDao sSrvsCronConfDao, SSrvsCronConf sSrvsCronConf, SSrvsCronHisDao sSrvsCronHisDao,SSrvsCronHis sSrvsCronHis, long startMills) throws SQLException {
		long endMills = System.currentTimeMillis();
		// 计算作业耗时
		BigDecimal costTime = new BigDecimal(endMills - startMills).divide(BigDecimal.valueOf(1000.00));


		//执行结束将定时任务状态更新为已安排并更新执行时间
		sSrvsCronConf.setJobState(ScheduleJobUtil.STATE_ARRANGED);
		sSrvsCronConf.setCostTime(costTime);
		sSrvsCronConfDao.updateByPk(sSrvsCronConf);
		//更新定时任务历史表
		sSrvsCronHis.setEndTime(TimeUtil.getCurrentDateTime());
		sSrvsCronHis.setCostTime(costTime);
		sSrvsCronHisDao.insertSSrvsCronHis(sSrvsCronHis);
	}

	/**
	 * 产生32位的UUID(JDK自带)
	 * @return
	 */
	private String createUUIDKey() {
		return UUID.randomUUID().toString().toUpperCase().replaceAll("-", "");
	}

}
